package com.lhl.jwt.autoconfigure;

import com.lhl.jwt.handler.*;
import com.lhl.jwt.security.configs.JwtAuthenticationConfigurer;
import com.lhl.jwt.security.configs.LoginConfigurer;
import com.lhl.jwt.security.configs.MobileConfigurer;
import com.lhl.jwt.security.configs.WxConfigurer;
import com.lhl.jwt.security.filters.OptionsRequestFilter;
import com.lhl.jwt.security.providers.JwtAuthenticationProvider;
import com.lhl.jwt.security.providers.MobileCodeAuthenticationProvider;
import com.lhl.jwt.security.providers.WxAuthenticationProvider;
import com.lhl.jwt.service.JwtUserDetailsService;
import io.swagger.annotations.Api;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.filter.CorsFilter;

/**
 * @className: com.lhl.jwt.autoconfigure.SecurityConfig
 * @description: TODO 类描述
 * @author: king
 * @date: 2020-12-22 10:12
 **/
@Api(tags = "SecurityConfig", produces = "SecurityConfig")
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableConfigurationProperties(JwtProperties.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static final String DEFAULT_PERMITURL = "/mobileCodeLogin/**,/wxLogin/**,/login/**,/logout/**";

    private final JwtUserDetailsService userDetailsService;

    private final JwtProperties jwtProperties;

    @Autowired
    public SecurityConfig(JwtUserDetailsService userDetailsService, JwtProperties jwtProperties) {
        this.userDetailsService = userDetailsService;
        this.jwtProperties = jwtProperties;
    }
    @Bean
    @ConditionalOnMissingBean
    protected JwtRefreshSuccessHandler jwtRefreshSuccessHandler() {
        JwtRefreshSuccessHandler refreshSuccessHandler = new JwtRefreshSuccessHandler(userDetailsService);
        refreshSuccessHandler.setTokenRefreshInterval(jwtProperties.getTokenRefreshInterval());
        return refreshSuccessHandler;
    }

    @Bean
    @ConditionalOnMissingBean
    public LoginSuccessHandler loginSuccessHandler() {
        return new LoginSuccessHandler(userDetailsService);
    }

    @Bean
    @ConditionalOnMissingBean
    public JwtAuthenticationFailureHandler jwtAuthenticationFailureHandler() {
        return new JwtAuthenticationFailureHandler();
    }

    @Bean
    @ConditionalOnMissingBean
    public TokenClearLogoutHandler tokenClearLogoutHandler() {
        return new TokenClearLogoutHandler(userDetailsService);
    }

    @Bean
    @ConditionalOnMissingBean
    public LogoutBlacklistHandler logoutBlacklistHandler() {
        return new LogoutBlacklistHandler();
    }


    @Bean("jwtAuthenticationProvider")
    public AuthenticationProvider getJwtAuthenticationProvider() {
        JwtAuthenticationProvider authenticationProvider = new JwtAuthenticationProvider(userDetailsService);
        return authenticationProvider;
    }

    @Bean("daoAuthenticationProvider")
    protected AuthenticationProvider getDaoAuthenticationProvider() throws Exception{
        DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider();
        daoProvider.setUserDetailsService(userDetailsService);
        daoProvider.setPasswordEncoder(new BCryptPasswordEncoder());
        return daoProvider;
    }

    @Bean("wxAuthenticationProvider")
    protected AuthenticationProvider getWxAuthenticationProvider() throws Exception{
        WxAuthenticationProvider wxProvider = new WxAuthenticationProvider(userDetailsService);
        return wxProvider;
    }

    @Bean("mobileAuthenticationProvider")
    protected AuthenticationProvider getMobileAuthenticationProvider() throws Exception{
        MobileCodeAuthenticationProvider mobileProvider = new MobileCodeAuthenticationProvider(userDetailsService);
        return mobileProvider;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(getJwtAuthenticationProvider())
                .authenticationProvider(getDaoAuthenticationProvider())
                .authenticationProvider(getWxAuthenticationProvider())
                .authenticationProvider(getMobileAuthenticationProvider());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        http.authorizeRequests()
//        http.cors(Customizer.withDefaults()).csrf().disable().authorizeRequests()
        http.cors().and().csrf().disable().authorizeRequests()
                //处理跨域请求中的Preflight请求
                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                .antMatchers(getPermitUrl()).permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin().disable()
                .addFilterAfter(new OptionsRequestFilter(), CorsFilter.class)
                .apply(new LoginConfigurer<>(userDetailsService)).authenticationSuccessHandler(loginSuccessHandler())
                .authenticationFailureHandler(jwtAuthenticationFailureHandler())
                .and()
                .addFilterAfter(new OptionsRequestFilter(), CorsFilter.class)
                .apply(new WxConfigurer<>()).authenticationSuccessHandler(loginSuccessHandler())
                .authenticationFailureHandler(jwtAuthenticationFailureHandler())
                .and()
                .addFilterAfter(new OptionsRequestFilter(), CorsFilter.class)
                .apply(new MobileConfigurer<>(userDetailsService)).authenticationSuccessHandler(loginSuccessHandler())
                .authenticationFailureHandler(jwtAuthenticationFailureHandler())
                .and()
                .apply(new JwtAuthenticationConfigurer<>())
                .authenticationSuccessHandler(jwtRefreshSuccessHandler())
                .authenticationFailureHandler(jwtAuthenticationFailureHandler())
                .permissiveRequestUrls(getPermitUrl())
                .and()
                .logout()
                .addLogoutHandler(tokenClearLogoutHandler())
                .addLogoutHandler(logoutBlacklistHandler())
                .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())
                .and()
                .sessionManagement().disable();
    }

    private String[] getPermitUrl() {
        String urls = jwtProperties.getPermitUrls() + "," + DEFAULT_PERMITURL;

        String[] strs = StringUtils.split(urls.trim(), ",");
        for (int i = 0; i < strs.length; i++) {
            strs[i] = strs[i].trim();
        }
        return StringUtils.split(urls.trim(), ",");
    }

}
